package com.ljg.activiti.d_processvariables;

import java.io.InputStream;
import java.util.List;

import org.activiti.engine.ProcessEngine;
import org.activiti.engine.ProcessEngines;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.TaskService;
import org.activiti.engine.history.HistoricVariableInstance;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.runtime.ProcessInstance;
import org.junit.Before;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 流程变量相关测试类
 * @author guo
 *
 */
public class ProcessVariablesTest {
	
	private Logger log = LoggerFactory.getLogger(ProcessVariablesTest.class);
	
	// 流程引擎对象
	private ProcessEngine processEngine;
	
	@Before
	public void before() {
		/**
		 * 创建默认的流程引擎对象，默认读取classpath下的activiti.cfg.xml创建流程引擎对象
		 */
		processEngine = ProcessEngines.getDefaultProcessEngine();
		log.info("创建流程引擎对象成功！");
	}
	
	/**
	 * 部署流程定义-从inputstream
	 * 部署完成之后，数据库表的变化
	 * 	-> `act_ge_bytearray`资源表中插入数据来保存流程的bpmn文件和png文件
	 *  -> `act_ge_property`配置表中会更新数据，下次insert数据的Id
	 *  -> `act_re_deployment`流程部署表中会插入数据，流程部署的ID，名称，部署时间等
	 *  -> `act_re_procdef`流程定义表中会插入数据，流程定义Id，流程定义Key，流程定义的名称，流程定义的版本，部署Id和资源文件名称等信息
	 *  
	 *  processEngine.getRepositoryService()：获取流程定义和流程部署相关的Service
	 */
	@Test
	public void deploymentProcessDefinition_inputstream() {
		InputStream bpmnIn = this.getClass().getClassLoader().getResourceAsStream("diagrams/processVariables.bpmn");
		InputStream pngIn = this.getClass().getClassLoader().getResourceAsStream("diagrams/processVariables.png");
		Deployment deployment = processEngine.getRepositoryService() // 获取流程定义和流程部署相关的Service
			.createDeployment() // 创建流程部署对象
			.addInputStream("processVariables.bpmn", bpmnIn) // 从InputStream中加载流程资源
			.addInputStream("processVariables.png", pngIn) // 
			.name("测试流程变量流程") // 添加部署的名称
			.deploy();
		
		log.info("部署完成，deploymentId=" + deployment.getId() + "，deploymentName=" + deployment.getName() + "，deploymentTime=" + deployment.getDeploymentTime());
	}
	
	/**
	 * 启动流程实例
	 * 启动流程实例之后，数据库表的变化
	 *  -> `act_ge_property`配置表中会更新数据，下次insert数据的Id
	 *  -> `act_hi_taskinst`历史任务表中会插入数据，流程定义Id，任务定义Id，流程实例Id，执行对象Id，任务名称，任务办理人，任务开始时间等信息
	 *  -> `act_hi_procinst`历史流程实例表中插入数据，流程实例Id，流程定义Id，开始时间，开始活动定义Id等信息
	 *  -> `act_hi_actinst`历史活动实例表中插入数据，流程定义Id，流程实例Id，执行对象Id，活动定义Id，任务Id，活动定义名称，任务办理人，开始结束时间等信息
	 *  -> `act_hi_identitylink`历史办理人表插入数据，组Id，办理人类型，办理人Id，流程实例Id等信息
	 *  -> `act_ru_execution`正在执行的执行对象表插入数据，执行对象Id，流程实例Id，流程定义Id，活动定义Id等信息
	 *  -> `act_ru_task`正在执行的任务表插入数据，任务Id，执行对象Id，流程实例Id，流程定义Id，当前任务名称，当前任务定义Id，当前任务办理人，创建时间等信息
	 *  -> `act_ru_identitylink`正在执行的办理人表插入数据，组Id，办理人类型，办理人Id，流程实例Id等信息
	 *  
	 * processEngine.getRuntimeService() // 获得正在执行的流程实例和执行对象相关的Service
	 */
	@Test
	public void startProcessInstance() {
		String processDefinitionKey = "processVariables"; // 流程定义的key对应bpmn文件中id的属性值
		ProcessInstance processInstance = processEngine.getRuntimeService() // 获得与正在执行的流程实例和执行对象相关的Service
			.startProcessInstanceByKey(processDefinitionKey); // 根据流程定义的key启动流程实例，默认是按照最新版本的流程定义启动
		log.info("启动流程成功，Id=" + processInstance.getId() + "，deploymentId=" + processInstance.getDeploymentId() + "，name=" + processInstance.getName() + "，processInstanceId=" + processInstance.getProcessInstanceId());
	}
	
	/**
	 * 设置流程变量
	 * 设置流程变量，数据库表的变化
	 * 	-> `act_ru_variable`正在执行的流程变量表插入数据，设置几个流程变量插入几条数据
	 *  -> `act_hi_varinst`历史的流程变量表插入数据
	 */
	@Test
	public void setProcessVariables() {
		// 与正在执行的任务相关的Service
		TaskService taskService = processEngine.getTaskService();
		
		String taskId = "50004"; // 任务Id
		/** 设置流程变量：使用基本数据类型  */
		/*taskService.setVariable(taskId, "leaveDay", 7);
		taskService.setVariable(taskId, "leaveDate", new Date());
		taskService.setVariable(taskId, "leaveReason", "回家探亲");*/
		
		/** 设置流程变量：使用javabean类型 
		 *  当一个javaBean（实现序列化）放置到流程变量中，要求javaBean的属性不能再发生变化，如果发生变化，在获取的时候会抛出异常
		 *  因此实现序列化的javabean需要生成一个版本Id
		 *  	private static final long serialVersionUID = -6903298022873297041L;
		 */
		Person p = new Person();
		p.setId(1L);
		p.setName("张三");
		taskService.setVariable(taskId, "personInfo", p);
		
		log.info("设置流程变量成功！");
	}
	
	/**
	 * 获取流程变量
	 */
	@Test
	public void getProcessVariables() {
		// 与正在执行的任务相关的Service
		TaskService taskService = processEngine.getTaskService();
		
		String taskId = "50004"; // 任务Id
		/** 获取流程变量：使用基本数据类型  */
//		Integer leaveDay = (Integer) taskService.getVariable(taskId, "leaveDay");
//		Date leaveDate = (Date) taskService.getVariable(taskId, "leaveDate");
//		String leaveReason = (String) taskService.getVariable(taskId, "leaveReason");
		
		// log.info("请假天数：" + leaveDay + "，请假日期：" + leaveDate + "，请假原因：" + leaveReason);
		
		/** 获取流程变量：使用javabean类型 */
		Person p = (Person) taskService.getVariable(taskId, "personInfo");
		log.info(p.getId() + "，" + p.getName());
	}
	
	/**
	 * 完成任务
	 */
	@Test
	public void completeMyPersonalTask() {
		String taskId = "55002";
		processEngine.getTaskService() // 获得与正在执行的任务管理相关的Service
			.complete(taskId);
		log.info("完成任务：任务Id=" + taskId);
	}
	
	/**
	 * 模拟设置和获取流程变量场景
	 */
	@SuppressWarnings("unused")
	public void setAndGetProcessVariables() {
		// 与正在执行的流程实例、执行对象相关Service
		RuntimeService runtimeService = processEngine.getRuntimeService();
		// 与正在执行的任务相关的Service
		TaskService taskService = processEngine.getTaskService();
		
		/** 设置流程变量  */
//		runtimeService.setVariable(executionId, variableName, value); // 表示使用执行对象的Id和流程变量的名称来设置流程变量的值 一次只能设置一个值
//		runtimeService.setVariables(executionId, variables); // 使用执行对象Id和Map集合设置流程变量，Map的key表示流程变量的名称，Map的value表示流程变量的值 一次可设置多个值
		
//		taskService.setVariable(taskId, variableName, value); // 表示使用任务的Id和流程变量的名称来设置流程变量的值 一次只能设置一个值
//		taskService.setVariables(taskId, variables); // 使用任务Id和Map集合设置流程变量，Map的key表示流程变量的名称，Map的value表示流程变量的值 一次可设置多个值
		
//		runtimeService.startProcessInstanceById(processDefinitionKey, variables); // 启动流程实例的同时可以设置流程变量，使用Map集合
//		taskService.complete(taskId, variables); // 完成任务的同时设置流程变量
		
//		setVariableLocal() 
		
		/** 获取流程变量 */
//		runtimeService.getVariable(executionId, variableName); // 使用执行对象Id和流程变量的名称获取流程变量
//		runtimeService.getVariables(executionId); // 使用执行对象Id获取流程变量，Map的key表示流程变量的名称，Map的value表示流程变量的值
//		runtimeService.getVariables(executionId, variableNames); // 使用执行对象Id获取流程变量的值，可以设置获取指定流程变量名称的流程变量的值 要获取的流程变量的名称封装到集合中
		
//		taskService.getVariable(taskId, variableName); // 使用任务Id和流程变量的名称获取流程变量
//		taskService.getVariables(taskId); // 使用任务Id获取流程变量，Map的key表示流程变量的名称，Map的value表示流程变量的值
//		taskService.getVariables(taskId, variableNames); // 使用任务Id获取流程变量的值，可以设置获取指定流程变量名称的流程变量的值 要获取的流程变量的名称封装到集合中
		
	}
	
	/**
	 * 查询流程变量历史表
	 */
	@Test
	public void findHistoryVariables() {
		String variableName = "leaveDay";
		List<HistoricVariableInstance> hvList = processEngine.getHistoryService()//
			.createHistoricVariableInstanceQuery() // 创建历史的流程变量查询
			.variableName(variableName) //
			.list();
		
		if(null == hvList || hvList.size() == 0) {
			log.error("没有查询到" + variableName + "的历史变量！");
			return;
		}
		
		for(HistoricVariableInstance hvi : hvList) {
			log.info("" + hvi.getId() + "----" + hvi.getProcessInstanceId() + "-----" + hvi.getVariableName() + "----" + hvi.getValue());
		}
	}
}
